package com.yc.cloud.portal.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.yc.cloud.basic.api.ResponseResult;
import com.yc.cloud.openai.dto.NlpChunkDto;
import com.yc.cloud.openai.dto.NlpChunkResultDto;
import com.yc.cloud.openai.dto.OpenAiResultDto;
import com.yc.cloud.openai.dto.resp.LinkAINLPStreamResp;
import com.yc.cloud.openai.request.OpenAiParam;
import com.yc.cloud.openai.service.NLPService;
import com.yc.cloud.portal.dto.request.AnswerCacheVoiceParam;
import com.yc.cloud.portal.dto.request.AnswerQueryParam;
import com.yc.cloud.portal.service.AnswerService;
import com.yc.cloud.portal.vo.AnswerVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.sql.Struct;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 答案库
 *
 * @author Yancy
 * @date 2024-01-31
 */
@RestController
@RequestMapping("/answer")
@Slf4j
@Api(value = "答案库", tags = "答案库接口")
public class AnswerController {

    @Resource
    private AnswerService answerService;

    @Resource
    @Qualifier("linkAINLPServiceImpl")
    private NLPService nlpService;

    @PostMapping("/import")
    @ApiOperation(value = "导入答题库", notes = "根据文档内容自动生成问题和答案,并生成对应的语音文件,返回一个修改后的文档内容")
    public ResponseResult<String> importQuestionAndAnswer(@Validated @RequestPart MultipartFile file) {
        val result = answerService.importQuestionAndAnswer(file);
        return result != null ? ResponseResult.success(result) : ResponseResult.failed();
    }

    @PostMapping("analysis2")
    @SneakyThrows
    @ApiOperation(value = "通过语音文件获取答案信息-测试用", notes = "通过问题编号获取答案信息")
    public ResponseResult<Map<Integer, List<AnswerVo>>> analysis2(@RequestPart MultipartFile file, OpenAiParam param) {
        param.setFormat(FileUtil.getSuffix(file.getOriginalFilename()));
        val result = answerService.analysis(file.getBytes(), param);
        return ResponseResult.success(result);
    }


    @GetMapping("query")
    @ApiOperation(value = "通过问题编号获取答案信息", notes = "通过问题编号获取答案信息")
    public ResponseResult<Map<Integer, List<AnswerVo>>> query(AnswerQueryParam param) {
        val result = answerService.query(param);
        return ResponseResult.success(result);
    }


    @SneakyThrows
    @PostMapping(value = "stream/analysis", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @ApiOperation(value = "流式语音识别", notes = "通过问题编号获取答案信息")
    public Flux<String> streamAnalysis(@RequestPart MultipartFile file, OpenAiParam param) {
        param.setStream(true);
        param.setFormat(FileUtil.getSuffix(file.getOriginalFilename()));
        return answerService.streamAnalysis(file.getBytes(), param);
    }

    @SneakyThrows
    @PostMapping(value = "stream/analysis2", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @ApiOperation(value = "流式语音识别-不包含音频数据", notes = "通过问题编号获取答案信息")
    public SseEmitter streamAnalysis3(@RequestPart MultipartFile file, OpenAiParam param) {
        param.setFormat(FileUtil.getSuffix(file.getOriginalFilename()));
        return answerService.streamAnalysis3(file.getBytes(), param);
    }

    @SneakyThrows
    @PostMapping(value = "voice/cache")
    @ApiOperation(value = "缓存答案语音内容", notes = "将答案为文本的内容,转换为语音内容后缓存到服务器上")
    public ResponseResult<Boolean> cacheVoiceContent(AnswerCacheVoiceParam param) {
        val result = answerService.cacheVoiceContent(param);
        return result ? ResponseResult.success(true) : ResponseResult.failed();
    }


    @PostMapping(value = "stream/analysis4", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @ApiOperation(value = "LinkAI流式语音识别-不包含音频数据", notes = "通过问题编号获取答案信息")
    public Flux<String> streamAnalysis2( OpenAiParam param) {
        String apiUrl = "https://api.link-ai.tech/v1/chat/completions";
        val webClient = WebClient.builder()
                .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/json")
                .build();
        param.setAppKey("Link_MDL66qSUECziluJX0Qq77A7RUT2oqRjkDbfxaaQJGc");
        val jsonArray = JSONUtil.createArray();
        jsonArray.add(JSONUtil.createObj().set("role", "user").set("content", param.getContent()));
        String requestBody = JSONUtil.createObj().set("stream", param.getStream()).set("app_code", param.getAppid()).set("messages", jsonArray).toString();
        val authorization = "Bearer " + param.getAppKey();
        return webClient.post()
                .uri(apiUrl)
                .header(HttpHeaders.AUTHORIZATION, authorization)
                .header("Content-Type", "application/json")
                .bodyValue(requestBody)
                .retrieve()
                .onStatus(HttpStatus::isError, clientResponse ->
                        clientResponse.bodyToMono(String.class)
                                .flatMap(responseBody -> {
                                    // 处理错误的响应体
                                    log.error("Error response body: {}", responseBody);
                                    return Mono.error(new RuntimeException("Error occurred during API call: " + responseBody));
                                })
                )
                .bodyToFlux(String.class);

    }

    @PostMapping(value = "stream/analysis5", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    @ApiOperation(value = "LinkAI流式语音识别-不包含音频数据", notes = "通过问题编号获取答案信息")
    public Flux<NlpChunkResultDto> streamAnalysis5(OpenAiParam param) {
        String apiUrl = "https://api.link-ai.tech/v1/chat/completions";
        val webClient = WebClient.builder()
                .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/json")
                .build();

        val jsonArray = JSONUtil.createArray();
        jsonArray.add(JSONUtil.createObj().set("role", "user").set("content", param.getContent()));
        String requestBody = JSONUtil.createObj().set("stream", param.getStream()).set("app_code", param.getAppid()).set("messages", jsonArray).toString();
        val authorization = "Bearer " + param.getAppKey();
// 创建一个线程安全的累加器
        StringBuilder totalResultBuilder = new StringBuilder();
        return webClient.post()
                .uri(apiUrl)
                .header(HttpHeaders.AUTHORIZATION, authorization)
                .header("Content-Type", "application/json")
                .bodyValue(requestBody)
                .retrieve()
                .onStatus(HttpStatus::isError, clientResponse ->
                        clientResponse.bodyToMono(String.class)
                                .flatMap(responseBody -> {
                                    // 处理错误的响应体
                                    log.error("Error response body: {}", responseBody);
                                    return Mono.error(new RuntimeException("Error occurred during API call: " + responseBody));
                                })
                )
                .bodyToFlux(String.class)
                .mapNotNull(chunk -> {
                    //log.info("Received chunk: {}", chunk);
                    //整个流结束
                    if ("[DONE]".equals(chunk)) {
                        val totalResult = totalResultBuilder.toString();
                        log.info("totalResultBuilder:{}", totalResult);
                        return new NlpChunkResultDto(true, "");
                    }
                    String data = parseChunk(chunk);
                    log.info("Received data: {}", data);
                    //把null值转为空字符串
                    if (StrUtil.isEmpty(data)) {
                        data = "";
                    }
                    totalResultBuilder.append(data);
                    return new NlpChunkResultDto(false, data);
                });
    }


    // JSON 解析方法
    private String parseChunk(String chunk) {
        val nlpChunkDto = JSONUtil.toBean(chunk, NlpChunkDto.class);
        val choiceList = nlpChunkDto.getChoices();
        if (CollUtil.isEmpty(choiceList)) {
            return null;
        }
        if (choiceList.get(0).getDelta() != null) {
            return choiceList.get(0).getDelta().getContent();
        }
        return null;
    }

}
